package com.xframe.common.core.aop;


import com.xframe.common.core.constant.ErrorConstant;
import com.xframe.common.core.dto.ErrorCode;
import com.xframe.common.core.exception.BusinessException;
import com.xframe.xdal.core.exception.DalFrameException;
import lombok.extern.log4j.Log4j2;

import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.List;

/**
 * AOP运行时
 *
 * @author xufeng
 * @version 1.0
 * @date 2022-09-28
 */
@Log4j2
public class XAOPRuntime {


    private static XAOPRuntime runtime = new XAOPRuntime();

    public static XAOPRuntime getInstance(){
        return runtime;
    }

    private XAOPRuntime(){
        decorationList = new ArrayList<>();
        decorationList.add(new XTransactionDecoration());
        decorationList.add(new ExecTimeDecoration());
    }

    private List<BaseDecoration> decorationList;

    public void registerDecoration(BaseDecoration decorationParem){
        if(decorationParem != null && !findCheck(decorationParem)) {
            decorationList.add(decorationParem);
        }
    }

    private boolean findCheck(BaseDecoration decorationParem){
        for(IMethodDecoration decoration : this.decorationList){
            if(decoration.getClass().equals(decorationParem.getClass())){
                return true;
            }
        }
        return false;
    }

    public Object execMethod(XAOPContext context){
        BaseDecoration curDecoration = null;
        try {
            List<BaseDecoration> decorations = findCurExecDecorationList(context);
            curDecoration = buildExecDecoration(decorations);
            if(curDecoration != null){
                curDecoration.init(context);
                curDecoration.beforeExecution(context);
            }
            Object resaultVal = context.getMethod().invoke(context.getExecObj(), context.getParArray());
            context.setExecResault(resaultVal);
            if(curDecoration != null){
                curDecoration.afterExecution(context);
                curDecoration.finallyComplete(context);
            }
            return context.getExecResault();

        }catch (IllegalArgumentException exception) {
            if(curDecoration != null){
                curDecoration.exceptionHandler(context);
            }
            // 方法执行参数错误
            throw new BusinessException(ErrorCode.METHOD_EXEC_ARGU_ERROR, ErrorConstant.METHOD_EXEC_ARGU_ERROR_MSG);
        } catch (IllegalAccessException e) {
            if(curDecoration != null){
                curDecoration.exceptionHandler(context);
            }
            // 方法执行权限不够
            throw new BusinessException(ErrorCode.METHOD_EXEC_FAIL, e.getMessage());
        } catch (InvocationTargetException e) {
            if(curDecoration != null){
                curDecoration.exceptionHandler(context);
            }
            // 方法执行失败
            if (DalFrameException.class.isAssignableFrom(e.getTargetException().getClass())) {
                throw new BusinessException(((DalFrameException) e.getTargetException()).getCode(), e.getTargetException().getMessage());
            }
            if (BusinessException.class.isAssignableFrom(e.getTargetException().getClass())) {
                throw new BusinessException(((BusinessException) e.getTargetException()).getCode(), e.getTargetException().getMessage());
            } else {
                throw new BusinessException(ErrorCode.METHOD_EXEC_FAIL, e.getTargetException().getMessage());
            }
        } catch (Exception exception) {
            if(curDecoration != null){
                curDecoration.exceptionHandler(context);
            }
            throw new BusinessException(ErrorCode.INTERNAL_SERVER_ERROR, exception.getMessage());
        }
    }

    private List<BaseDecoration> findCurExecDecorationList(XAOPContext context){

        List<BaseDecoration> decorations = new ArrayList<>();
        for(BaseDecoration decoration : this.decorationList){
            BaseDecoration baseDecoration = decoration.buildeNew();
            if(baseDecoration.testMethod(context)){
                decorations.add(baseDecoration);
            }
        }
        return decorations;
    }

    private BaseDecoration buildExecDecoration(List<BaseDecoration> baseDecorations){
        BaseDecoration curDecoration = null;

        if(baseDecorations != null && baseDecorations.size() > 0){
            curDecoration = baseDecorations.get(0);
            BaseDecoration tmp = curDecoration;
            for(int i = 1; i < baseDecorations.size(); i++){
                tmp.setDecoration(baseDecorations.get(i));
                tmp = baseDecorations.get(i);
            }
        }
        return curDecoration;
    }


}
